#include "Thread.hpp"
using namespace ThreadModule; // 放开，std没有放开，只会找到我自己实现的Thread
#include <vector>
#include <mutex> //C++使用锁的头文件
#include"LockGuard.hpp"

// // 线程数据对象， 2. -----采用静态或全局锁的方式----
// class ThreadData
// {
// public:
//     ThreadData(int &g_tickets, const std::string &name)
//         : _g_tickets(g_tickets), _name(name), _total(0)
//     {
//     }
//     ~ThreadData()
//     {
//     }

// public:
//     int &_g_tickets;   // 共用同一个全局变量票数
//     std::string _name; // 线程名
//     int _total;        // 该线程抢到了多少张票
// };

// //全局或静态锁
// pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// // 线程执行的函数
// void route(ThreadData *td)
// {
//     // 加锁
//     while (true)
//     {
//         // 访问临界资源的代码，叫做临界区，我们加锁的本质就是把并行执行变成串行执行，加锁粒度越细越好
//         //  加锁 : 竞争锁是自由竞争的，竞争锁的能力太强的线程，会导致其他线程抢不到锁 --- 造成了其他线程的饥饿问题！
//         //pthread_mutex_lock(&mutex);
//         //加锁, RAII方式，临时对象实现加锁解锁
//         LockGuard guard(&mutex);    //有名对象，临时对象，构造加锁，析构下次循环重新临时对象
//         if (td->_g_tickets > 0)
//         {
//             // 模仿抢票过程
//             usleep(1000); // 休息一毫秒
//             printf("%s running, get tickets: %d\n", td->_name.c_str(), td->_g_tickets);
//             td->_g_tickets--;                  // 抢票成功
//             //pthread_mutex_unlock(&mutex); // 抢票成功后，就可以解锁
//             td->_total++;
//         }
//         else
//         {
//             //pthread_mutex_unlock(&mutex); // 解锁
//             break;
//         }
//     }
// }

// // 总票数
// int g_tickets = 1000;

// const int num = 4; // 创建4个线程
// int main()
// {
//     std::vector<Thread<ThreadData *>> threads; // 存放线程对象
//     std::vector<ThreadData *> datas;           // 存放每个线程对象里面存的数据对象（抢了多少票）
//     // 1.创建一批线程
//     for (int i = 0; i < num; i++)
//     {
//         std::string name = "thread-" + std::to_string(i + 1); // 线程名
//         // 每个线程配备一个线程数据对象，记录该线程抢到了多少张票
//         ThreadData *td = new ThreadData(g_tickets, name); // 每个线程对象共同引用一个票数变量
//         datas.emplace_back(td);                                  // 保存所有数据统计结果
//         threads.emplace_back(route, td, name);
//     }
//     // 2. 启动一批线程
//     for (auto &thread : threads)
//     {
//         thread.start();
//     }
//     // 3. 等待一批线程
//     for (auto &thread : threads)
//     {
//         thread.join();
//     }
//     sleep(1);
//     // 4. 输出统计数据
//     for (auto &data : datas)
//     {
//         std::cout << data->_name << ":" << data->_total << std::endl;
//         // 释放线程数据对象，不然内存泄漏
//         delete data;
//     }
//     return 0;
// }

//-------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------

// // 线程数据对象， 1.----采用局部锁的方式-----
// class ThreadData
// {
// public:
//     ThreadData(int &g_tickets, const std::string &name, pthread_mutex_t &mutex)
//         : _g_tickets(g_tickets), _name(name), _total(0), _mutex(mutex)
//     {
//     }
//     ~ThreadData()
//     {
//     }

// public:
//     int &_g_tickets;         // 共用同一个全局变量票数
//     std::string _name;       // 线程名
//     int _total;              // 该线程抢到了多少张票
//     pthread_mutex_t &_mutex; // 所有线程共同引用同一把局部锁
// };

// // 线程执行的函数
// void route(ThreadData *td)
// {
//     // 加锁
//     while (true)
//     {
//         // 访问临界资源的代码，叫做临界区，我们加锁的本质就是把并行执行变成串行执行，加锁粒度越细越好
//         //  加锁 : 竞争锁是自由竞争的，竞争锁的能力太强的线程，会导致其他线程抢不到锁 --- 造成了其他线程的饥饿问题！
//         pthread_mutex_lock(&td->_mutex);
//         if (td->_g_tickets > 0)
//         {
//             // 模仿抢票过程
//             usleep(1000); // 休息一毫秒
//             printf("%s running, get tickets: %d\n", td->_name.c_str(), td->_g_tickets);
//             td->_g_tickets--;                  // 抢票成功
//             pthread_mutex_unlock(&td->_mutex); // 抢票成功后，就可以解锁
//             td->_total++;
//         }
//         else
//         {
//             pthread_mutex_unlock(&td->_mutex); // 解锁
//             break;
//         }
//     }
// }

// // 总票数
// int g_tickets = 1000;

// const int num = 4; // 创建4个线程
// int main()
// {
//     // 创建局部锁，并初始化
//     pthread_mutex_t mutex;
//     pthread_mutex_init(&mutex, nullptr);

//     std::vector<Thread<ThreadData *>> threads; // 存放线程对象
//     std::vector<ThreadData *> datas;           // 存放每个线程对象里面存的数据对象（抢了多少票）
//     // 1.创建一批线程
//     for (int i = 0; i < num; i++)
//     {
//         std::string name = "thread-" + std::to_string(i + 1); // 线程名
//         // 每个线程配备一个线程数据对象，记录该线程抢到了多少张票
//         ThreadData *td = new ThreadData(g_tickets, name, mutex); // 每个线程对象共同引用一个票数变量
//         datas.emplace_back(td);                                  // 保存所有数据统计结果
//         threads.emplace_back(route, td, name);
//     }
//     // 2. 启动一批线程
//     for (auto &thread : threads)
//     {
//         thread.start();
//     }
//     // 3. 等待一批线程
//     for (auto &thread : threads)
//     {
//         thread.join();
//     }
//     sleep(1);
//     // 4. 输出统计数据
//     for (auto &data : datas)
//     {
//         std::cout << data->_name << ":" << data->_total << std::endl;
//         // 释放线程数据对象，不然内存泄漏
//         delete data;
//     }
//     // 释放锁
//     pthread_mutex_destroy(&mutex);
//     return 0;
// }

//----------------------------------------------------------------------------------------------------
// 以上是模拟抢票实现加锁，保证原子性，实现互斥，解决数据不一致问题
//---------------------------------------------------------------------------------------------------

// const int threadnum = 4;

// void Print(int &cnt)
// {
//     while (cnt)
//     {
//         std::cout << "hello I am myself thread, cnt: " << cnt-- << std::endl;
//         sleep(1);
//     }
// }

// int main()
// {
//     int a = 10;
//     std::vector<Thread<int>> threads;
//     // 创建一批线程
//     for (int i = 0; i < threadnum; i++)
//     {
//         std::string name = "thread-" + std::to_string(i + 1);
//         threads.emplace_back(Print, a, name); // 所有线程引用的都是同一个a变量，因为构造的时候所有都是用引用实现的
//     }
//     // 启动一批线程
//     for (auto &thread : threads)
//     {
//         thread.start();
//     }
//     // 回收一批线程
//     for (auto &thread : threads)
//     {
//         thread.join();
//         std::cout << "wait thread done, thread is: " << thread.name() << std::endl;
//     }
//     return 0;
// }

//-----------------------------------------------------------------------------------------------------------
// 以上是模拟C++封装原生线程库pthred
//-----------------------------------------------------------------------------------------------------------

// #include <iostream>
// #include <pthread.h> //原生线程库
// #include <sys/types.h>
// #include <unistd.h>
// #include <cstring>
// #include <vector>
// #include <string>
// #include <thread>

// // 线程是可以分离的: 默认线程是joinable的。
// // 如果我们main thread不关心新线程的执行信息，我们可以将新线程设置为分离状态:
// // 你是如何理解线程分离的呢？底层依旧属于同一个进程！(和之前讲的一样）只是不需要等待了
// // 一般都希望mainthread 是最后一个退出的，无论是否是join、detach

// //__thread   线程局部存储，可以让一个共享资源，变成每个线程都私有一份
// __thread int g_val = 100;
// // int g_val = 100;

// // 线程1g_val不变
// void *threadrun1(void *args)
// {
//     pthread_detach(pthread_self());
//     std::string name = static_cast<char *>(args);
//     int cnt = 5;
//     while (true)
//     {
//         g_val--;
//         std::cout << "I am " << name << "cnt:" << cnt << "g_val: " << g_val << "&g_val: " << &g_val << std::endl;
//         sleep(1);
//     }
//     // //异常退出了
//     // int* p = nullptr;
//     // *p = 10;
//     return nullptr;
// }
// // 线程2 g_val--
// void *threadrun2(void *args)
// {
//     // 线程分离
//     pthread_detach(pthread_self());
//     std::string name = static_cast<char *>(args);
//     int cnt = 5;
//     while (true)
//     {
//         g_val += 10;
//         std::cout << "I am " << name << "cnt:" << cnt << "g_val: " << g_val << "&g_val: " << &g_val << std::endl;
//         sleep(1);
//     }
//     return nullptr;
// }

// int main()
// {
//     // 创建线程并分离，1. 比main thread先退出不用回收，系统回收
//     //  2.比main thread后退出，main thread退了进程就退了，也是直接终止
//     //  3. 分离的线程异常了，进程退出, 所有线程都退出了
//     pthread_t tid;
//     pthread_create(&tid, nullptr, threadrun1, (void *)"thread-1");
//     pthread_create(&tid, nullptr, threadrun2, (void *)"thread-2");
//     // //线程分离
//     // pthread_detach(tid);
//     // int cnt = 2;
//     int cnt = 10;
//     while (true)
//     {
//         sleep(1);
//         // cnt--;
//         // if (!cnt)
//         // {
//         //     std::cout << "main thread quit...." << std::endl;
//         //     break;
//         // }
//     }
//     // 线程分离不用阻塞回收了    。。。
//     // 回收线程
//     // pthread_join(tid, nullptr);
//     return 0;
// }

// void threadrun(int num)
// {
//     while (num)
//     {
//         std::cout << "I am thread, num:" << num << std::endl;
//         sleep(1);
//     }
// }

// // 使用一下C++多线程封装了pthread库之后，怎么使用? 非常简单
// int main()
// {
//     // 创建多个线程
//     std::thread t1(threadrun, 10);
//     std::thread t2(threadrun, 10);
//     std::thread t3(threadrun, 10);
//     std::thread t4(threadrun, 10);
//     std::thread t5(threadrun, 10);
//     while (true)
//     {
//         std::cout << "I am main thread...." << std::endl;
//         sleep(1);
//     }
//     // 回收多个线程
//     t1.join();
//     t2.join();
//     t3.join();
//     t4.join();
//     t5.join();

//     return 0;
// }

//------------------------------------------------------------------------------------------------
// 以上是线程分离部分，知识验证代码
//------------------------------------------------------------------------------------------------

// const int threadnums = 5; // 创建线程个数

// // Task对象定义, 用来获取数据并执行任务返回结果的
// class Task
// {
// public:
//     Task(int x, int y)
//         : _datax(x), _datay(y)
//     {
//     }
//     // 执行任务并返回结果
//     int Excute()
//     {
//         return _datax + _datay;
//     }
//     ~Task()
//     {
//     }

// private:
//     int _datax;
//     int _datay;
// };

// // Result对象定义, 用来保存线程名，和该线程执行任务所得到的结果
// class Result
// {
// public:
//     Result(const string &name, int res)
//         : _threadname(name), _result(res)
//     {
//     }
//     // 查看结果接口
//     void Print()
//     {
//         cout << "_threadname:" << _threadname << "result:" << _result << endl;
//     }
//     ~Result()
//     {
//     }

// private:
//     string _threadname;
//     int _result;
// };

// // 以下是定义ThreadData对象， 用来存该线程名，和该线程执行任务的数据，代表该线程对象
// class ThreadData
// {
// public:
//     ThreadData(int x, int y, const string &name)
//         : _t(x, y), _threadname(name)
//     {
//     }
//     // 获取该线程对象名
//     string threadname()
//     {
//         return _threadname;
//     }
//     // 获取该线程对象执行任务的结果
//     int run()
//     {
//         return _t.Excute(); // 该线程对象做加法任务是调用Task对象做的
//     }
//     ~ThreadData()
//     {
//     }

// private:
//     string _threadname;
//     Task _t; // 该线程需要做的任务
// };

// void *handlerTask(void *args)
// {
//     ThreadData *td = static_cast<ThreadData *>(args); // 强转类型回来
//     string threadname = td->threadname();             // 获取线程名
//     int result = td->run();                           // 执行任务，得到结果
//     // 该线程函数结束后，释放对应线程对象
//     delete td;

//     Result *res = new Result(threadname, result); // res也是new出来的，等会要释放
//     return (void *)res;                           // 线程结束返回任务执行结果
// }

// int main()
// {
//     // 多线程创建
//     vector<pthread_t> threads;
//     for (int i = 0; i < threadnums; i++)
//     {
//         pthread_t tid;
//         char threadname[64];
//         snprintf(threadname, 64, "thread-%d", i + 1);
//         // 每创建一个线程对应执行流执行操作自己的线程对象，所以堆上，创建一个线程对象
//         ThreadData *td = new ThreadData(10, 20, threadname);
//         pthread_create(&tid, nullptr, handlerTask, td);
//         threads.push_back(tid);
//     }

//     void *ret = nullptr;
//     // 保存所有的线程结果
//     vector<Result *> result_set;
//     // 开始回收所有创建的线程, 并拿到每个线程执行结果
//     for (auto &tid : threads)
//     {
//         pthread_join(tid, &ret); // 输出型参数ret带出结果
//         result_set.push_back((Result *)ret);
//     }
//     // 打印所有的结果
//     for (auto &res : result_set)
//     {
//         res->Print(); // 打印每个线程执行结果
//         cout << endl;
//         delete res; // 打印一个线程结果就释放一个
//     }
//     return 0;
// }

//------------------------------------------------------------------------------------------------------
// 多线程创建第二部分
//------------------------------------------------------------------------------------------------------

// //可重入函数
// void* handlertask(void* args)
// {
//     string threadname = static_cast<char*>(args);   //将参数类型转换回来
//     while(true)
//     {
//         cout<<"I am"<<threadname<<endl;
//         sleep(1);
//     }
//     return nullptr;
// }

// int main()
// {
//     //多线程创建
//     vector<pthread_t> threads;  //组织管理多线程
//     for(int i = 0;i<threadnum;i++)
//     {
//         //带上线程名，作为参数传入新线程
//         //char threadname[64];  错误，因为每个线程共用同一份threadname的话，下一个线程创建就会覆盖之前线程对应保存的线程名
//         //每个线程创建之前，都在堆上开辟自己一块空间用来存储自己的线程名，使用
//         char* threadname = new char[64];
//         snprintf(threadname, 64, "thread-%d",i+1);
//         pthread_t tid;
//         pthread_create(&tid, nullptr, handlertask, threadname);
//         threads.push_back(tid);
//     }
//     //回收创建的线程
//     for(auto& tid:threads)
//     {
//         pthread_join(tid, nullptr);
//     }
//     return 0;
// }

//-------------------------------------------------------------------------------------------------------------
// 以下是线程第一部分
//----------------------------------------------------------------------------------------------------------------

// // 转16进制输出
// string ToHex(pthread_t tid)
// {
//     char id[64];
//     snprintf(id, strlen(id), "0X%lx", tid); // 十六进制输出字符串
//     return id;
// }

// int g_val = 0;

// // 新线程执行流执行这个函数
// void *threadRun(void *args)
// {
//     string str = (char *)args; // 接收参数
//     int cnt = 5;
//     while (cnt--)
//     {
//         //新主线程，共享同一份进程资源的（小demo）
//         g_val++;
//         cout<<"g_val:"<<g_val<<"&g_cal:"<<&g_val<<endl;
//         // cout << str << "new thread is running..." << "cnt:" << cnt << "--pid:" << getpid() << "myself thread id:" << ToHex(pthread_self())
//         //      << endl;
//         sleep(1);
//     }
//     //异常直接进程退出
//     // int* ptr = nullptr;
//     // *ptr = 10;
//     // 线程函数结束
//     // return 返回值
//     // pthread_exit((void*)123);
//     return (void *)123;
// }

// int main()
// {
//     // 创建一个新线程
//     pthread_t tid; // pthread_t 其实是一个无符号长整型
//     pthread_create(&tid, nullptr, threadRun, (void *)"thread-1");
//     sleep(2);
//     //直接将指定线程退出，有点像以前的kill
//     pthread_cancel(tid);     //退出值为-1  其实就是宏PTHREAD_CANCLE
//     // 新和主线程那个先运行，不确定，由系统调度决定
//     // main主线程一旦退出 == 进程退出 == 所有线程退出
//     //所以main主线程退出前，回收其它线程
//     //一旦某个线程异常退出 == 进程退出 == 所有线程退出
//     //线程也要"wait"，不然会导致内存泄漏问题
//     // int cnt = 10;
//     // while (cnt--)
//     // {
//     //     cout<<"g_val:"<<g_val<<"&g_cal:"<<&g_val<<endl;
//     //     // cout << "main thread is running..." << "cnt:" << cnt << "--pid:" << getpid()
//     //     //      << "myself thread id:" << ToHex(pthread_self()) << "new thread id:" << ToHex(tid) << endl;
//     //     sleep(1);
//     // }
//     //注意线程退出不考虑异常情况，
//     //因为一旦异常，说明信号，既然信号，本进程退出了因为线程只是进程的一个内部分支，
//     //进程退出父进程接收

//     void* ret = nullptr;
//     //回收线程, 并带出返回值
//     int n = pthread_join(tid, &ret);
//     cout<<"main thread quit...n:"<<n<<"main thread get a return val:"<<(long long)ret<<endl;
//     return 0;
// }

// 线程数据对象， 2. -----采用静态或全局锁的方式----
class ThreadData
{
public:
    ThreadData(int &g_tickets, const std::string &name)
        : _g_tickets(g_tickets), _name(name), _total(0)
    {
    }
    ~ThreadData()
    {
    }

public:
    int &_g_tickets;   // 共用同一个全局变量票数
    std::string _name; // 线程名
    int _total;        // 该线程抢到了多少张票
};

//全局或静态锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// 线程执行的函数
void route(ThreadData *td)
{
    // 加锁
    while (true)
    {
        // 访问临界资源的代码，叫做临界区，我们加锁的本质就是把并行执行变成串行执行，加锁粒度越细越好
        //  加锁 : 竞争锁是自由竞争的，竞争锁的能力太强的线程，会导致其他线程抢不到锁 --- 造成了其他线程的饥饿问题！
        //pthread_mutex_lock(&mutex);
        //加锁, RAII方式，临时对象实现加锁解锁
        LockGuard guard(&mutex);    //有名对象，临时对象，构造加锁，析构下次循环重新临时对象
        if (td->_g_tickets > 0)
        {
            // 模仿过程
            usleep(1000); // 休息一毫秒
            printf("%s running, get tickets: %d\n", td->_name.c_str(), td->_g_tickets);
            td->_g_tickets--;                  
            //pthread_mutex_unlock(&mutex); 
            td->_total++;
        }
        else
        {
            //pthread_mutex_unlock(&mutex); // 解锁
            break;
        }
    }
}
int g_tickets = 1000;
const int num = 4; // 创建4个线程
int main()
{
    std::vector<Thread<ThreadData *>> threads; // 存放线程对象
    std::vector<ThreadData *> datas;           // 存放每个线程对象里面存的数据对象
    // 1.创建一批线程
    for (int i = 0; i < num; i++)
    {
        std::string name = "thread-" + std::to_string(i + 1); // 线程名
        // 每个线程配备一个线程数据对象
        ThreadData *td = new ThreadData(g_tickets, name); // 每个线程对象共同引用一个票数变量
        datas.emplace_back(td);                                  // 保存所有数据统计结果
        threads.emplace_back(route, td, name);
    }
    // 2. 启动一批线程
    for (auto &thread : threads)
    {
        thread.start();
    }
    // 3. 等待一批线程
    for (auto &thread : threads)
    {
        thread.join();
    }
    sleep(1);
    // 4. 输出统计数据
    for (auto &data : datas)
    {
        std::cout << data->_name << ":" << data->_total << std::endl;
        // 释放线程数据对象，不然内存泄漏
        delete data;
    }
    return 0;
}